package com.perstorp.cykelapp;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

import org.json.JSONObject;
import com.google.android.gms.maps.CameraUpdateFactory;
import com.google.android.gms.maps.GoogleMap;
import com.google.android.gms.maps.GoogleMap.InfoWindowAdapter;
import com.google.android.gms.maps.MapFragment;
import com.google.android.gms.maps.model.BitmapDescriptorFactory;
import com.google.android.gms.maps.model.LatLng;
import com.google.android.gms.maps.model.Marker;
import com.google.android.gms.maps.model.MarkerOptions;
import com.google.android.gms.maps.model.PolylineOptions;
import com.perstorp.cykelapp.R;
import android.app.ActionBar;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.Fragment;
import android.app.FragmentTransaction;
import android.app.ActionBar.OnNavigationListener;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.res.Resources;
import android.graphics.Color;
import android.graphics.SweepGradient;
import android.location.Criteria;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.os.AsyncTask;
import android.os.Bundle;
import android.provider.Settings;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.TextView;
import android.widget.Toast;

public class GMapFragment extends Fragment implements OnNavigationListener,
		LocationListener {

	static final LatLng PERSTORP = new LatLng(56.137778, 13.395);
	private GoogleMap map;
	private View rootView;
	private String[] actionBarDropDownMenu;
	private String[] actionBarDropDownMenuSubs;
	private LocationManager locationManager;
	private String provider;
	private Marker userLocation = null;
	private ArrayList<LatLng> markerPoints;
	private GetMapCoordinates getCoordinate;

	public static GMapFragment newInstance() {
		GMapFragment fragment = new GMapFragment();
		return fragment;
	}

	public View onCreateView(LayoutInflater inflater, ViewGroup container,
			Bundle savedInstanceState) {
		rootView = inflater.inflate(R.layout.fragment_map, container, false);
		setHasOptionsMenu(true); // Enable options-menu
		setUpMapIfNeeded(); // Setting up the map and adding markers
		createCustomMarkers(); // Setting up custom map-marker-info view
		setUpLocation(); // Setup our location and place a marker where we are
		markerPoints = new ArrayList<LatLng>();
		getCoordinate = new GetMapCoordinates(rootView);

		return rootView;
	}

	private String getDirectionsUrl(LatLng origin, LatLng dest) {
		// Origin of route
		String str_origin = "origin=" + origin.latitude + ","
				+ origin.longitude;

		// Destination of route
		String str_dest = "destination=" + dest.latitude + "," + dest.longitude;

		// Sensor enabled
		String sensor = "sensor=false";

		// Building the parameters to the web service
		String parameters = str_origin + "&" + str_dest + "&" + sensor;

		// Output format
		String output = "json";

		// Building the url to the web service
		String url = "https://maps.googleapis.com/maps/api/directions/"
				+ output + "?" + parameters;

		return url;
	}

	// A method to download json data from url
	private String downloadUrl(String strUrl) throws IOException {
		String data = "";
		InputStream iStream = null;
		HttpURLConnection urlConnection = null;
		try {
			URL url = new URL(strUrl);

			// Creating an http connection to communicate with url
			urlConnection = (HttpURLConnection) url.openConnection();

			// Connecting to url
			urlConnection.connect();

			// Reading data from url
			iStream = urlConnection.getInputStream();

			BufferedReader br = new BufferedReader(new InputStreamReader(
					iStream));

			StringBuffer sb = new StringBuffer();

			String line = "";
			while ((line = br.readLine()) != null) {
				sb.append(line);
			}

			data = sb.toString();

			br.close();

		} catch (Exception e) {
			Log.d("Exception while downloading url", e.toString());
		} finally {
			iStream.close();
			urlConnection.disconnect();
		}
		return data;
	}

	// Fetches data from url passed
	private class DownloadTask extends AsyncTask<String, Void, String> {
		// Downloading data in non-ui thread
		@Override
		protected String doInBackground(String... url) {

			// For storing data from web service
			String data = "";

			try {
				// Fetching the data from web service
				data = downloadUrl(url[0]);
			} catch (Exception e) {
				Log.d("Background Task", e.toString());
			}
			return data;
		}

		// Executes in UI thread, after the execution of
		// doInBackground()
		@Override
		protected void onPostExecute(String result) {
			super.onPostExecute(result);

			ParserTask parserTask = new ParserTask();

			// Invokes the thread for parsing the JSON data
			parserTask.execute(result);

		}
	}

	// A class to parse the Google Places in JSON format
	private class ParserTask extends
			AsyncTask<String, Integer, List<List<HashMap<String, String>>>> {

		// Parsing the data in non-ui thread
		@Override
		protected List<List<HashMap<String, String>>> doInBackground(
				String... jsonData) {
			JSONObject jObject;
			List<List<HashMap<String, String>>> routes = null;

			try {
				jObject = new JSONObject(jsonData[0]);
				DirectionsJSONParser parser = new DirectionsJSONParser();

				// Starts parsing data
				routes = parser.parse(jObject);
			} catch (Exception e) {
				e.printStackTrace();
			}
			return routes;
		}

		// Executes in UI thread, after the parsing process
		@Override
		protected void onPostExecute(List<List<HashMap<String, String>>> result) {
			ArrayList<LatLng> points = null;
			PolylineOptions lineOptions = null;
			MarkerOptions markerOptions = new MarkerOptions();
			String distance = "";
			String duration = "";

			if (result.size() < 1) {
				Toast.makeText(rootView.getContext(), "No Points",
						Toast.LENGTH_SHORT).show();
				return;
			}

			// Traversing through all the routes
			for (int i = 0; i < result.size(); i++) {
				points = new ArrayList<LatLng>();
				lineOptions = new PolylineOptions();

				// Fetching i-th route
				List<HashMap<String, String>> path = result.get(i);

				// Fetching all the points in i-th route
				for (int j = 0; j < path.size(); j++) {
					HashMap<String, String> point = path.get(j);

					if (j == 0) { // Get distance from the list
						distance = point.get("distance");
						continue;
					} else if (j == 1) { // Get duration from the list
						duration = point.get("duration");
						continue;
					}
					double lat = Double.parseDouble(point.get("lat"));
					double lng = Double.parseDouble(point.get("lng"));
					LatLng position = new LatLng(lat, lng);
					points.add(position);
				}

				// Adding all the points in the route to LineOptions
				lineOptions.addAll(points);
				lineOptions.width(5);
				lineOptions.color(Color.RED);
			}

			// MainActivity.this.tvDistanceDuration.setText("Distance:" +
			// distance + ", Duration:" + duration);

			// Drawing polyline in the Google Map for the i-th route
			map.addPolyline(lineOptions);
		}
	}

	private void setUpLocation() {
		rootView.getContext();
		LocationManager service = (LocationManager) rootView.getContext()
				.getSystemService(Context.LOCATION_SERVICE);
		boolean enabledGPS = service
				.isProviderEnabled(LocationManager.GPS_PROVIDER);
		boolean enabledWiFi = service
				.isProviderEnabled(LocationManager.NETWORK_PROVIDER);

		// Check if enabled and if not send user to the GSP settings
		// Better solution would be to display a dialog and suggesting to
		// go to the settings
		if (!enabledGPS) {
			Toast.makeText(rootView.getContext(), "GPS signal not found",
					Toast.LENGTH_LONG).show();
			final Intent intent = new Intent(
					Settings.ACTION_LOCATION_SOURCE_SETTINGS);
			popUpMesseage("Enable GPS?", "Do you want to enable the GPS?",
					false, intent, "Yes", "No");

		}

		locationManager = (LocationManager) rootView.getContext()
				.getSystemService(Context.LOCATION_SERVICE);
		// Define the criteria how to select the locatioin provider -> use
		// default
		Criteria criteria = new Criteria();
		provider = locationManager.getBestProvider(criteria, false);
		Location location = locationManager.getLastKnownLocation(provider);

		// Initialize the location fields
		if (location != null) {
			onLocationChanged(location);
		} else {
			// do something
		}
	}

	private void popUpMesseage(String title, String message,
			boolean setCancelble, final Intent intent, String setPosButton,
			String setNegButton) {
		AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(
				rootView.getContext());

		// set title
		alertDialogBuilder.setTitle(title);

		// set dialog message
		alertDialogBuilder
				.setMessage(message)
				.setCancelable(setCancelble)
				.setPositiveButton(setPosButton,
						new DialogInterface.OnClickListener() {
							public void onClick(DialogInterface dialog, int id) {
								// if this button is clicked, start intent
								startActivity(intent);
							}
						})
				.setNegativeButton(setNegButton,
						new DialogInterface.OnClickListener() {
							public void onClick(DialogInterface dialog, int id) {
								// if this button is clicked, just close
								// the dialog box and do nothing
								dialog.cancel();
							}
						});

		// create alert dialog
		AlertDialog alertDialog = alertDialogBuilder.create();

		// show it
		alertDialog.show();
	}

	private void createCustomMarkers() {
		map.setInfoWindowAdapter(new InfoWindowAdapter() {

			@Override
			public View getInfoWindow(Marker arg0) {

				LayoutInflater inflater = LayoutInflater.from(rootView
						.getContext());
				View v = inflater.inflate(R.layout.custom_map_marker_info_view,
						null);

				// Getting the position from the marker
				LatLng latLng = arg0.getPosition();

				// Getting reference to the TextView to set latitude
				TextView tvLat = (TextView) v.findViewById(R.id.tv_lat);

				// Getting reference to the TextView to set longitude
				TextView tvLng = (TextView) v.findViewById(R.id.tv_lng);

				// Setting the latitude
				tvLat.setText("Latitude: " + latLng.latitude);

				// Setting the longitude
				tvLng.setText("Longitude: " + latLng.longitude);

				// Returning the view containing InfoWindow contents
				return v;
			}

			@Override
			public View getInfoContents(Marker arg0) {
				return null;
			}
		});
	}

	@Override
	public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
		inflater.inflate(R.menu.gmap, menu);
		restoreActionBar();
		super.onCreateOptionsMenu(menu, inflater);
	}

	public void restoreActionBar() {
		ActionBar actionBar = ((Activity) rootView.getContext()).getActionBar();
		actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_LIST);
		actionBar.setDisplayShowTitleEnabled(false);
		// actionBar.setTitle(mTitle);

		// Gets the strings and substrings from the XML file and puts them in
		// the custom adapter
		Resources res = getResources();
		actionBarDropDownMenu = res.getStringArray(R.array.ab_drop_down_menu);
		actionBarDropDownMenuSubs = res
				.getStringArray(R.array.ab_drop_down_menu_subs);
		ArrayAdapter<String> adapter;
		adapter = new MyAdapter(rootView.getContext(),
				R.layout.custom_actionbar_spinner_layout, actionBarDropDownMenu);

		actionBar.setListNavigationCallbacks(adapter, this);
	}

	@Override
	public boolean onNavigationItemSelected(int itemPosition, long itemId) {
		// Initializing

		// Already two locations
		if (markerPoints.size() > 1) {
			map.clear();
			markerPoints.clear();
			setUpCustomMarkers();
		}

		// Adding new items to the ArrayList

		switch (itemPosition) {
		case 1:
			markerPoints = getCoordinate.getCoordinates(itemPosition - 1);
			break;
		case 2:
			markerPoints = getCoordinate.getCoordinates(itemPosition); 
			break;
		case 3:
			markerPoints = getCoordinate.getCoordinates(itemPosition + 1);
			break;
		case 4:
			markerPoints = getCoordinate.getCoordinates(itemPosition + 2);
			break;
		default:
			break;
		}

		// Creating MarkerOptions
		MarkerOptions options = new MarkerOptions();

		for (int i = 0; i < markerPoints.size(); i++) {
			// Setting the position of the marker
			options.position(markerPoints.get(i));

			/**
			 * For the start location, the color of marker is GREEN and for the
			 * end location, the color of marker is RED.
			 */
			if (i == 0) {
				options.icon(BitmapDescriptorFactory
						.defaultMarker(BitmapDescriptorFactory.HUE_GREEN));
			} else if (i == 1) {
				options.icon(BitmapDescriptorFactory
						.defaultMarker(BitmapDescriptorFactory.HUE_RED));
			}

			// Add new marker to the Google Map Android API V2
			map.addMarker(options);
		}

		// Checks, whether start and end locations are captured
		if (markerPoints.size() >= 2) {
			LatLng origin = markerPoints.get(0);
			LatLng dest = markerPoints.get(1);

			// Getting URL to the Google Directions API
			String url = getDirectionsUrl(origin, dest);

			DownloadTask downloadTask = new DownloadTask();

			// Start downloading json data from Google Directions API
			downloadTask.execute(url);
		}
		return false;
	}

	@Override
	public boolean onOptionsItemSelected(MenuItem item) {
		// Handle item selection and set map-type
		switch (item.getItemId()) {
		case R.id.normal:
			map.setMapType(GoogleMap.MAP_TYPE_NORMAL);
			return true;
		case R.id.satallite:
			map.setMapType(GoogleMap.MAP_TYPE_SATELLITE);
			return true;
		case R.id.Terrain:
			map.setMapType(GoogleMap.MAP_TYPE_TERRAIN);
			return true;
		case R.id.clearMap:
			map.clear();
			setUpCustomMarkers();
			return true;
		default:
			return super.onOptionsItemSelected(item);
		}
	}

	private void setUpMapIfNeeded() {
		// If the map is not yet been initialize or fragment has been removed,
		// get the map again
		if (map == null) {
			map = ((MapFragment) getFragmentManager()
					.findFragmentById(R.id.map)).getMap();
		}
		if (map != null) {
			// If the map has been initialize, add markers to put on the map and
			// setting starting-point/zoom
			setUpCustomMarkers();
			// Enable the map to provide my location
			map.setMyLocationEnabled(true);
			// Move the camera instantly to Perstorp with a zoom of 15.
			map.moveCamera(CameraUpdateFactory.newLatLngZoom(PERSTORP, 15));
			// Zoom in, animating the camera.
			map.animateCamera(CameraUpdateFactory.zoomTo(13), 2000, null);
		}
	}

	public void setUpCustomMarkers() {
		Marker perstorp = map.addMarker(new MarkerOptions()
				.position(PERSTORP)
				.title("Perstorp")
				.snippet("Perstorp is cool")
				.icon(BitmapDescriptorFactory
						.fromResource(R.drawable.ic_beenhere_black_18dp)));
	}

	@Override
	public void onDestroy() {
		super.onDestroy();
		Fragment fragment = (getFragmentManager().findFragmentById(R.id.map));
		FragmentTransaction ft = getActivity().getFragmentManager()
				.beginTransaction();
		ft.remove(fragment);
		ft.commit();
	}

	// Class that fixes our custom action-bar drop-down-menu
	public class MyAdapter extends ArrayAdapter<String> {
		public MyAdapter(Context ctx, int txtViewResourceId, String[] objects) {
			super(ctx, txtViewResourceId, objects);
		}

		@Override
		public View getDropDownView(int position, View cnvtView, ViewGroup prnt) {
			return getCustomView(position, cnvtView, prnt);
		}

		@Override
		public View getView(int pos, View cnvtView, ViewGroup prnt) {
			return getCustomView(pos, cnvtView, prnt);
		}

		public View getCustomView(int position, View convertView,
				ViewGroup parent) {
			LayoutInflater inflater = LayoutInflater
					.from(rootView.getContext());
			View mySpinner = inflater.inflate(R.layout.custom_actionbar_spinner_layout,
					parent, false);
			TextView main_text = (TextView) mySpinner
					.findViewById(R.id.text_main_seen);

			main_text.setText(actionBarDropDownMenu[position]);
			TextView subSpinner = (TextView) mySpinner
					.findViewById(R.id.sub_text_seen);
			subSpinner.setText(actionBarDropDownMenuSubs[position]);
			// ImageView left_icon = (ImageView) mySpinner
			// .findViewById(R.id.left_pic);
			// left_icon.setImageResource(total_images[position]);
			return mySpinner;
		}
	}

	// Request updates at startup
	@Override
	public void onResume() {
		super.onResume();
		locationManager.requestLocationUpdates(provider, 400, 1, this);
	}

	// Remove the location-listener updates when Activity is paused
	@Override
	public void onPause() {
		super.onPause();
		locationManager.removeUpdates(this);
	}

	@Override
	public void onLocationChanged(Location location) {
		double lat = location.getLatitude();
		double lng = location.getLongitude();
		// Toast.makeText(rootView.getContext(), "Location " + lat + "," + lng,
		// Toast.LENGTH_LONG).show();
		LatLng coordinate = new LatLng(lat, lng);
		// Toast.makeText(rootView.getContext(),
		// "Location " + coordinate.latitude + "," + coordinate.longitude,
		// Toast.LENGTH_LONG).show();

		if (userLocation != null)
			userLocation.remove();
		userLocation = map.addMarker(new MarkerOptions()
				.position(coordinate)
				.title("You are here!")
				.snippet("Here you are")
				.icon(BitmapDescriptorFactory
						.fromResource(R.drawable.ic_launcher)));

	}

	@Override
	public void onStatusChanged(String provider, int status, Bundle extras) {
	}

	@Override
	public void onProviderEnabled(String provider) {
		Toast.makeText(rootView.getContext(),
				"Enabled new provider " + provider, Toast.LENGTH_SHORT).show();

	}

	@Override
	public void onProviderDisabled(String provider) {
		Toast.makeText(rootView.getContext(), "Disabled provider " + provider,
				Toast.LENGTH_SHORT).show();

	}
}
